Skip to content

feat(Table): advance search dialog support UseSearchForm parameter#7783

Merged
ArgoZhang merged 35 commits intomainfrom
feat-table-search
Mar 22, 2026
Merged

feat(Table): advance search dialog support UseSearchForm parameter#7783
ArgoZhang merged 35 commits intomainfrom
feat-table-search

Conversation

@ArgoZhang
Copy link
Member

@ArgoZhang ArgoZhang commented Mar 22, 2026

Link issues

fixes #7782

Summary By Copilot

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

☑️ Self Check before Merge

⚠️ Please check all items below before review. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Merge the latest code from the main branch

Summary by Sourcery

Add support for using the SearchForm-based advanced search dialog in tables, introduce metadata-driven search item handling with reset capabilities, and align search-related APIs and naming across components.

New Features:

  • Enable tables to use a SearchForm-backed advanced search dialog via the UseSearchForm option and propagate search filters from dialogs to table queries.
  • Support automatic construction of search metadata from property types and localized options for search items and table columns.
  • Allow SearchDialog to host a SearchForm directly with configurable search items, localization options, and filter change callbacks.

Enhancements:

  • Rename and standardize search metadata types and properties from *MetaData to *Metadata and MetaData to Metadata across components and tests.
  • Refine SearchForm to own filter construction internally, expose a simplified OnChanged callback, and support localization through SearchFormLocalizerOptions.
  • Update dialog and table infrastructure to auto-generate editor/search items when templates are not provided and to better handle reset behavior for search filters.
  • Improve sample table search UI layout with a responsive grid and expose a toggle for using the SearchForm-based search in demos.

Documentation:

  • Extend the table search samples to demonstrate the new SearchForm-based search mode and updated configuration options.

Tests:

  • Expand unit tests for SearchForm, SearchDialog, Table, and search metadata types, including new coverage for localization options, metadata inference, reset behavior, and dialog interactions.
  • Add new tests for EditDialog auto-generated items and SearchDialog operating in UseSearchForm mode with filter propagation.

Copilot AI review requested due to automatic review settings March 22, 2026 07:04
@bb-auto bb-auto bot added the enhancement New feature or request label Mar 22, 2026
@bb-auto bb-auto bot added this to the v10.4.0 milestone Mar 22, 2026
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Mar 22, 2026

Reviewer's Guide

Refactors the search-form metadata model and integrates SearchForm-based advanced search into Table and SearchDialog, adding reset support and localization-aware metadata building while updating tests, samples, and APIs to the new conventions.

Sequence diagram for Table advanced search using SearchForm in dialog

sequenceDiagram
    actor User
    participant Table as Table~TItem~
    participant DialogService
    participant SearchDialog as SearchDialog~TItem~
    participant SearchForm
    participant ISearchItemExt as ISearchItemExtensions

    User->>Table: click search button
    Table->>Table: ShowSearchDialog()
    alt UseSearchForm is true
        Table->>DialogService: ShowSearchDialog(CreateSearchFormDialog())
        DialogService->>SearchDialog: create with UseSearchForm=true
        SearchDialog->>SearchForm: render with SearchItems, OnChanged
        loop for each ISearchItem
            SearchForm->>ISearchItemExt: BuildSearchMetadata(options)
            ISearchItemExt-->>SearchForm: ISearchFormItemMetadata
            SearchForm->>ISearchItemExt: CreateSearchItemComponentByMetadata()
        end

        User->>SearchForm: modify search fields
        SearchForm->>SearchForm: Item.Metadata.ValueChanged invoked
        SearchForm->>SearchForm: Items.ToFilter()
        SearchForm-->>SearchDialog: OnChanged(FilterKeyValueAction)
        SearchDialog-->>Table: OnSearchFormFilterChanged(action)
        Table->>Table: _advanceSearchFilter = action

        User->>SearchDialog: click search button
        SearchDialog-->>Table: OnSearchClick
        Table->>Table: SearchClick()
        Table->>Table: GetAdvanceSearches()
        Table->>Table: _advanceSearchFilter.ToSearches()
        Table-->>User: results filtered
    else UseSearchForm is false
        Table->>DialogService: ShowSearchDialog with model based options
        DialogService->>SearchDialog: create with editor form
        User->>SearchDialog: search using model fields
        SearchDialog-->>Table: OnSearchClick
        Table-->>User: results filtered
    end
Loading

Class diagram for updated search metadata model and integration

classDiagram
    direction LR

    class ISearchFormItemMetadata {
        <<interface>>
        +string PlaceHolder
        +Func~Task~ ValueChanged
        +FilterLogic FilterLogic
        +FilterAction FilterAction
        +Func~object, FilterKeyValueAction~ GetFilterCallback
        +FilterKeyValueAction GetFilter(string fieldName)
        +void Reset()
    }

    class SearchMetadataBase {
        <<abstract>>
        +string PlaceHolder
        +Func~Task~ ValueChanged
        +FilterLogic FilterLogic
        +FilterAction FilterAction
        +Func~object, FilterKeyValueAction~ GetFilterCallback
        +FilterKeyValueAction GetFilter(string fieldName)
        +FilterKeyValueAction CreateFilter(string fieldName, object value)
        +void Reset()
    }

    class StringSearchMetadata {
        +string Value
        +FilterKeyValueAction GetFilter(string fieldName)
        +Task ValueChangedHandler(string value)
        +void Reset()
    }

    class MultipleStringSearchMetadata {
        +FilterKeyValueAction GetFilter(string fieldName)
    }

    class SelectSearchMetadata {
        +IEnumerable~SelectedItem~ Items
    }

    class MultipleSelectSearchMetadata {
        +FilterKeyValueAction GetFilter(string fieldName)
    }

    class CheckboxListSearchMetadata {
    }

    class NumberSearchMetadata {
        +string StartValue
        +string EndValue
        +string StartValueLabelText
        +string EndValueLabelText
        +Type ValueType
        +FilterKeyValueAction GetFilter(string fieldName)
        +Task StartValueChangedHandler(string value)
        +Task EndValueChangedHandler(string value)
        +void Reset()
    }

    class DateTimeSearchMetadata {
        +DateTime Value
        +FilterKeyValueAction GetFilter(string fieldName)
        +Task ValueChangedHandler(DateTime value)
        +void Reset()
    }

    class DateTimeRangeSearchMetadata {
        +DateTimeRangeValue Value
        +FilterKeyValueAction GetFilter(string fieldName)
        +Task ValueChangedHandler(DateTimeRangeValue value)
        +void Reset()
    }

    class ISearchItem {
        <<interface>>
        +string FieldName
        +Type PropertyType
        +string Text
        +bool? ShowLabel
        +bool? ShowLabelTooltip
        +int Cols
        +ISearchFormItemMetadata Metadata
        +FilterKeyValueAction GetFilter()
        +void Reset()
    }

    class SearchItem {
        +string FieldName
        +Type PropertyType
        +string Text
        +bool? ShowLabel
        +bool? ShowLabelTooltip
        +int Cols
        +ISearchFormItemMetadata Metadata
        +FilterKeyValueAction GetFilter()
        +void Reset()
    }

    class ITableColumn {
        <<interface>>
        +string FieldName
        +Type PropertyType
        +bool? Searchable
        +ISearchFormItemMetadata SearchFormItemMetadata
    }

    class TableColumn~TItem,TType~ {
        +ISearchFormItemMetadata SearchFormItemMetadata
    }

    class InternalTableColumn {
        +ISearchFormItemMetadata SearchFormItemMetadata
    }

    class ISearchItemExtensions {
        <<static>>
        +ISearchFormItemMetadata BuildSearchMetadata(SearchFormLocalizerOptions options)
        +RenderFragment CreateSearchItemComponentByMetadata()
    }

    class ITableColumnExtensions {
        <<static>>
        +ISearchItem ParseSearchItem(SearchFormLocalizerOptions options)
        +ISearchFormItemMetadata BuildSearchMetadata(SearchFormLocalizerOptions options)
    }

    class SearchForm {
        +IEnumerable~ISearchItem~ Items
        +bool ShowLabel
        +bool ShowLabelTooltip
        +EditorFormRowType RowType
        +int ItemsPerRow
        +bool ShowUnsetGroupItemsOnTop
        +EditorFormGroupType GroupType
        +SearchFormLocalizerOptions SearchFormLocalizerOptions
        +Func~FilterKeyValueAction, Task~ OnChanged
        +FilterKeyValueAction AutoGenerateTemplate(ISearchItem item)
    }

    class SearchDialogOption~TModel~ {
        +bool UseSearchForm
        +List~ISearchItem~ SearchItems
        +Func~FilterKeyValueAction, Task~ OnFilterChanged
        +SearchFormLocalizerOptions SearchFormLocalizerOptions
    }

    class SearchDialog~TModel~ {
        +bool UseSearchForm
        +List~ISearchItem~ SearchItems
        +Func~FilterKeyValueAction, Task~ OnChanged
        +SearchFormLocalizerOptions SearchFormLocalizerOptions
        +Task OnSearchFormFilterChanged(FilterKeyValueAction action)
    }

    class Table~TItem~ {
        +bool UseSearchForm
        +List~ISearchItem~ SearchFormItems
        +FilterKeyValueAction _searchFilter
        +FilterKeyValueAction _advanceSearchFilter
        +Task ShowSearchDialog()
        +Task OnSearchFormFilterChanged(FilterKeyValueAction action)
        +List~IFilterAction~ GetAdvanceSearches()
    }

    ISearchFormItemMetadata <|.. SearchMetadataBase
    SearchMetadataBase <|-- StringSearchMetadata
    StringSearchMetadata <|-- MultipleStringSearchMetadata
    StringSearchMetadata <|-- SelectSearchMetadata
    SelectSearchMetadata <|-- MultipleSelectSearchMetadata
    MultipleSelectSearchMetadata <|-- CheckboxListSearchMetadata
    SearchMetadataBase <|-- NumberSearchMetadata
    SearchMetadataBase <|-- DateTimeSearchMetadata
    SearchMetadataBase <|-- DateTimeRangeSearchMetadata

    ISearchItem <|.. SearchItem
    ISearchItem --> ISearchFormItemMetadata : Metadata

    ITableColumn --> ISearchFormItemMetadata : SearchFormItemMetadata
    TableColumn~TItem,TType~ ..|> ITableColumn
    InternalTableColumn ..|> ITableColumn

    ISearchItemExtensions ..> ISearchItem : extension
    ISearchItemExtensions ..> ISearchFormItemMetadata : builds

    ITableColumnExtensions ..> ITableColumn : extension
    ITableColumnExtensions ..> ISearchItem : creates
    ITableColumnExtensions ..> ISearchFormItemMetadata : builds

    SearchForm o--> ISearchItem : Items
    SearchForm ..> ISearchFormItemMetadata : uses via Metadata

    SearchDialogOption~TModel~ o--> ISearchItem : SearchItems
    SearchDialogOption~TModel~ ..> SearchFormLocalizerOptions

    SearchDialog~TModel~ o--> ISearchItem : SearchItems
    SearchDialog~TModel~ ..> SearchForm : renders

    Table~TItem~ o--> ISearchItem : SearchFormItems
    Table~TItem~ ..> SearchDialogOption~TItem~ : creates
Loading

File-Level Changes

Change Details Files
Refactor search metadata APIs and ISearchItem to use new Metadata model with reset and automatic metadata construction.
  • Replace ISearchFormItemMetaData with ISearchFormItemMetadata, SearchMetaDataBase with SearchMetadataBase, and rename concrete *MetaData classes to *Metadata while adding Reset implementations.
  • Extend ISearchItem and SearchItem with Metadata property, Reset method, BuildSearchMetadata helper, CreateSearchItemComponentByMetadata renderer, and ToFilter extension for collections.
  • Update SearchForm to build metadata on demand using localized SearchFormLocalizerOptions, wire OnChanged callbacks to emit FilterKeyValueAction via Items.ToFilter, and remove Filter/FilterChanged parameters.
src/BootstrapBlazor/Components/Searches/SearchMetadataBase.cs
src/BootstrapBlazor/Components/Searches/StringSearchMetadata.cs
src/BootstrapBlazor/Components/Searches/NumberSearchMetadata.cs
src/BootstrapBlazor/Components/Searches/DateTimeSearchMetadata.cs
src/BootstrapBlazor/Components/Searches/DateTimeRangeSearchMetadata.cs
src/BootstrapBlazor/Components/Searches/MultipleSelectSearchMetadata.cs
src/BootstrapBlazor/Components/Searches/MultipleStringSearchMetadata.cs
src/BootstrapBlazor/Components/Searches/CheckboxListSearchMetadata.cs
src/BootstrapBlazor/Components/Searches/SelectSearchMetadata.cs
src/BootstrapBlazor/Components/Searches/ISearchFormItemMetadata.cs
src/BootstrapBlazor/Components/SearchForm/SearchItem.cs
src/BootstrapBlazor/Components/SearchForm/ISearchItem.cs
src/BootstrapBlazor/Extensions/ISearchItemExtensions.cs
src/BootstrapBlazor/Components/SearchForm/SearchForm.razor.cs
Integrate SearchForm-based advanced search into Table and SearchDialog, including dialog wiring, filter propagation, and reset behavior.
  • Table now supports UseSearchForm for both inline and popup advanced search, manages _searchItems and _advanceSearchFilter, builds SearchFormItems with localized metadata, and resets search items on ResetSearchClick.
  • SearchDialog gains UseSearchForm, SearchItems, OnChanged, and SearchFormLocalizerOptions parameters, and when enabled renders a SearchForm (with shared button template) instead of EditorForm/body template.
  • DialogServiceExtensions and SearchDialogOption pass through new SearchForm-related settings; DialogBase now exposes GetItemsByColumns and EditDialog moves model-null check and auto item generation into OnParametersSet.
src/BootstrapBlazor/Components/Table/Table.razor
src/BootstrapBlazor/Components/Table/Table.razor.Search.cs
src/BootstrapBlazor/Components/Dialog/SearchDialog.razor
src/BootstrapBlazor/Components/Dialog/SearchDialog.razor.cs
src/BootstrapBlazor/Components/Dialog/SearchDialogOption.cs
src/BootstrapBlazor/Extensions/DialogServiceExtensions.cs
src/BootstrapBlazor/Components/Dialog/DialogBase.cs
src/BootstrapBlazor/Components/Dialog/EditDialog.razor.cs
Update table column API and utilities to use new search metadata naming and behavior.
  • Rename ITableColumn.SearchFormItemMetaData to SearchFormItemMetadata across implementations and attributes, and adjust InternalTableColumn and AutoGenerateColumnAttribute accordingly.
  • Update ITableColumnExtensions to build metadata via BuildSearchMetadata, respect custom SearchFormItemMetadata, and construct appropriate concrete metadata types based on column type and lookup configuration.
  • Ensure Utility.GenerateColumns materializes to a list and that ParseSearchItem sets SearchItem.Metadata using the new builder.
src/BootstrapBlazor/Components/Table/ITableColumn.cs
src/BootstrapBlazor/Components/Table/TableColumn.cs
src/BootstrapBlazor/Components/Table/InternalTableColumn.cs
src/BootstrapBlazor/Attributes/AutoGenerateColumnAttribute.cs
src/BootstrapBlazor/Extensions/ITableColumnExtensions.cs
src/BootstrapBlazor/Utils/Utility.cs
Enhance samples, layout CSS, and localization for the new SearchForm-based table search UX.
  • Extend TablesSearch sample to demonstrate UseSearchForm with both column-level SearchFormItemMetadata and external SearchItems, and to toggle UseSearchForm via a new switch in an updated .bb-grid layout.
  • Rename sample backing fields to *_SearchFormItemMetadata and introduce _searchItems list used when UseSearchForm is enabled.
  • Add bb-grid / bb-grid-item CSS utilities for responsive switch layout and update locale JSON stubs to align with new texts (diff shows file touched).
src/BootstrapBlazor.Server/Components/Samples/Table/TablesSearch.razor
src/BootstrapBlazor.Server/Components/Samples/Table/TablesSearch.razor.cs
src/BootstrapBlazor.Server/wwwroot/css/site.css
src/BootstrapBlazor.Server/Locales/en-US.json
src/BootstrapBlazor.Server/Locales/zh-CN.json
Align unit tests with the new metadata model and add coverage for SearchForm, SearchDialog, EditDialog, and Table advanced search with SearchForm.
  • Rename and adjust SearchFormItemMetadata tests, updating types and method names; add Reset and ValueType coverage for new metadata classes.
  • Update SearchFormTest to use Metadata, OnChanged, SearchFormLocalizerOptions, ToFilter, and BuildSearchMetadata flows, and expand TableDialogTest to cover UseSearchForm in advanced search dialog including filter/reset behavior.
  • Adjust existing tests for table, auto-generate attributes, internal table column, and ITableColumnExtensions to use SearchFormItemMetadata naming; add new SearchDialogTest and EditDialogTest for UseSearchForm and auto-generated Items behavior.
  • Modernize some async assertions (e.g., WaitForAssertionAsync) to stabilize dialog tests.
test/UnitTest/Components/SearchFormItemMetadataTest.cs
test/UnitTest/Components/SearchFormTest.cs
test/UnitTest/Components/TableDialogTest.cs
test/UnitTest/Components/TableTest.cs
test/UnitTest/Extensions/ITableColumnExtensionsTest.cs
test/UnitTest/Attributes/AutoGenerateClassTest.cs
test/UnitTest/Components/InternalTableColumnTest.cs
test/UnitTest/Components/SearchDialogTest.cs
test/UnitTest/Components/EditDialogTest.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#7782 Modify the Table advanced search dialog so that when UseSearchForm is true, the advanced search popup uses a SearchForm-based UI (instead of model-based templates) and integrates its filters into the table search behavior.
#7782 Add and wire supporting APIs (SearchDialog, SearchDialogOption, DialogService, SearchForm, search metadata) plus tests and samples so the UseSearchForm-based advanced search flow is fully supported and verifiable.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In Table<TItem>.GetAdvanceSearches you call _advanceSearchFilter.ToSearches() when UseSearchForm is true without guarding for _advanceSearchFilter being null; consider null-checking or returning an empty list to avoid a potential NullReferenceException when no advanced filter has been set yet.
  • In SearchForm, GetSearchOptions caches the first computed SearchFormLocalizerOptions instance in _options and never refreshes it; if the SearchFormLocalizerOptions parameter is updated after initial render, those changes will be ignored, so consider invalidating _options when the parameter changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `Table<TItem>.GetAdvanceSearches` you call `_advanceSearchFilter.ToSearches()` when `UseSearchForm` is true without guarding for `_advanceSearchFilter` being null; consider null-checking or returning an empty list to avoid a potential `NullReferenceException` when no advanced filter has been set yet.
- In `SearchForm`, `GetSearchOptions` caches the first computed `SearchFormLocalizerOptions` instance in `_options` and never refreshes it; if the `SearchFormLocalizerOptions` parameter is updated after initial render, those changes will be ignored, so consider invalidating `_options` when the parameter changes.

## Individual Comments

### Comment 1
<location path="src/BootstrapBlazor/Components/Table/Table.razor.Search.cs" line_range="307" />
<code_context>
         }

-        SearchDialogOption<TItem> CreateModelDialog() => new()
+        SearchDialogOption<ITableSearchModel> CreateCustomerModelDialog() => new()
         {
-            Class = "modal-dialog-table",
</code_context>
<issue_to_address>
**issue (complexity):** Consider refactoring the new search dialog and search-form state logic into shared helpers to avoid duplication and make the flow easier to follow and maintain.

You can reduce the added complexity without changing behavior by centralizing the shared logic and encapsulating search-form-specific state.

### 1. Unify dialog construction

`CreateCustomerModelDialog`, `CreateSearchFormDialog`, and `CreateModelDialog` all set the same properties. You can introduce a small generic helper that configures the common bits once and then only set the deltas:

```csharp
private SearchDialogOption<TModel> CreateSearchDialogOption<TModel>(
    Action<SearchDialogOption<TModel>> configure)
{
    var option = new SearchDialogOption<TModel>
    {
        IsScrolling = ScrollingDialogContent,
        Title = SearchModalTitle,
        OnResetSearchClick = ResetSearchClick,
        OnSearchClick = SearchClick,
        RowType = SearchDialogRowType,
        ItemsPerRow = SearchDialogItemsPerRow,
        LabelAlign = SearchDialogLabelAlign,
        Size = SearchDialogSize,
        IsDraggable = SearchDialogIsDraggable,
        ShowMaximizeButton = SearchDialogShowMaximizeButton,
        ShowUnsetGroupItemsOnTop = ShowUnsetGroupItemsOnTop
    };

    configure(option);
    return option;
}
```

Then the factory methods become very small and harder to drift:

```csharp
SearchDialogOption<ITableSearchModel> CreateCustomerModelDialog() =>
    CreateSearchDialogOption<ITableSearchModel>(opt =>
    {
        opt.Model = CustomerSearchModel;
        opt.DialogBodyTemplate = CustomerSearchTemplate;
    });

SearchDialogOption<TItem> CreateModelDialog() =>
    CreateSearchDialogOption<TItem>(opt =>
    {
        opt.Class = "modal-dialog-table";
        opt.Model = SearchModel;
        opt.DialogBodyTemplate = SearchTemplate;
        opt.Items = Columns.Where(i => i.GetSearchable());
    });

SearchDialogOption<TItem> CreateSearchFormDialog() =>
    CreateSearchDialogOption<TItem>(opt =>
    {
        opt.Class = "modal-dialog-table modal-dialog-search-form";
        opt.DialogBodyTemplate = SearchTemplate;
        opt.UseSearchForm = true;
        opt.SearchFormLocalizerOptions = SearchFormLocalizerOptions;
        opt.SearchItems = SearchFormItems;
        opt.OnFilterChanged = action =>
        {
            _advanceSearchFilter = action;
            return Task.CompletedTask;
        };
    });
```

`ShowSearchDialog` then stays as-is but becomes easier to read since each factory is trivial.

---

### 2. Encapsulate search-form state

Instead of `_searchItems`, `_searchFilter`, `_advanceSearchFilter` plus helper methods, you can wrap them in a small helper to simplify lifecycle reasoning:

```csharp
private sealed class SearchFormState
{
    public List<ISearchItem>? Items { get; private set; }
    public FilterKeyValueAction? Filter { get; set; }
    public FilterKeyValueAction? AdvancedFilter { get; set; }

    public List<ISearchItem> EnsureItems(Func<List<ISearchItem>> factory)
    {
        Items ??= factory();
        return Items;
    }

    public void Reset()
    {
        Filter = null;
        AdvancedFilter = null;

        if (Items != null)
        {
            foreach (var item in Items)
            {
                item.Reset();
            }
            Items = null;
        }
    }
}
```

Use a single field:

```csharp
private readonly SearchFormState _searchFormState = new();

private List<ISearchItem> SearchFormItems =>
    _searchFormState.EnsureItems(GetSearchFormItems);

private Task OnSearchFormFilterChanged(FilterKeyValueAction action)
{
    _searchFormState.Filter = action;
    return Task.CompletedTask;
}
```

In `CreateSearchFormDialog` you then bind to the encapsulated state:

```csharp
SearchDialogOption<TItem> CreateSearchFormDialog() =>
    CreateSearchDialogOption<TItem>(opt =>
    {
        // ...
        opt.SearchItems = SearchFormItems;
        opt.OnFilterChanged = action =>
        {
            _searchFormState.AdvancedFilter = action;
            return Task.CompletedTask;
        };
    });
```

---

### 3. Simplify reset logic

With the helper, `ResetSearchClick` can delegate the search-form specifics to a single method:

```csharp
protected async Task ResetSearchClick()
{
    await ToggleLoading(true);

    if (UseSearchForm)
    {
        _searchFormState.Reset();
    }
    else if (CustomerSearchModel != null)
    {
        CustomerSearchModel.Reset();
    }
    else if (OnResetSearchAsync != null)
    {
        await OnResetSearchAsync(SearchModel);
    }
    else if (SearchTemplate == null)
    {
        Utility.Reset(SearchModel, CreateSearchModel());
    }

    await SearchClick();
    await ToggleLoading(false);
}
```

This keeps `ResetSearchClick` as a high-level orchestrator and removes field-level manipulation from that method.

---

### 4. Make `GetAdvanceSearches` safe and centralized

`GetAdvanceSearches` currently assumes `_advanceSearchFilter` is non-null in search-form mode. A small guard plus the encapsulated state improves robustness:

```csharp
protected List<IFilterAction> GetAdvanceSearches()
{
    if (UseSearchForm)
    {
        return _searchFormState.AdvancedFilter?.ToSearches()
               ?? new List<IFilterAction>();
    }

    var searches = new List<IFilterAction>();
    // existing logic...
}
```

This removes the hidden null assumption while keeping behavior consistent (no advanced filters → empty list).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class UseSearchForm support to Table’s advanced search dialog flow, and modernizes the SearchForm/search-metadata APIs to support auto-generated metadata, reset behavior, and updated callback patterns.

Changes:

  • Add UseSearchForm + SearchItems wiring for Table advanced search dialogs via SearchDialog and DialogServiceExtensions.
  • Rename “MetaData” types/properties to “Metadata”, introduce Reset() on search metadata, and update parsing/building helpers.
  • Update unit tests, samples, and localization strings to match the new SearchForm/SearchDialog APIs and naming.

Reviewed changes

Copilot reviewed 42 out of 42 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/UnitTest/Extensions/ITableColumnExtensionsTest.cs Updates tests for renamed SearchFormItemMetadata and metadata types.
test/UnitTest/Components/TableTest.cs Updates Table tests to use new SearchFormItemMetadata/SearchItem.Metadata APIs.
test/UnitTest/Components/TableDialogTest.cs Improves async assertions and adds coverage for SearchForm-based advanced search dialog behavior.
test/UnitTest/Components/SearchFormTest.cs Updates to new OnChanged/Metadata APIs; adds tests for localization options and ToFilter().
test/UnitTest/Components/SearchFormItemMetadataTest.cs Renames tests/types to *Metadata and validates new metadata behavior.
test/UnitTest/Components/SearchDialogTest.cs New tests for SearchDialog in UseSearchForm mode.
test/UnitTest/Components/InternalTableColumnTest.cs Updates reflection-based property set to SearchFormItemMetadata.
test/UnitTest/Components/EditDialogTest.cs New basic coverage for rendering EditDialog.
test/UnitTest/Attributes/AutoGenerateClassTest.cs Updates attribute interface tests for renamed metadata property/type.
src/BootstrapBlazor/Utils/Utility.cs Forces materialization of generated columns (ToList()).
src/BootstrapBlazor/Extensions/ITableColumnExtensions.cs Renames/updates search-item building to Metadata and supports SearchFormItemMetadata.
src/BootstrapBlazor/Extensions/ISearchItemExtensions.cs Adds metadata inference/building, component selection by metadata, and ToFilter() helper.
src/BootstrapBlazor/Extensions/DialogServiceExtensions.cs Passes new SearchForm-related parameters into SearchDialog.
src/BootstrapBlazor/Components/Table/TableColumn.cs Renames column parameter to SearchFormItemMetadata.
src/BootstrapBlazor/Components/Table/Table.razor.Search.cs Adds SearchForm-driven advanced search dialog path and reset behavior for SearchItems/metadata.
src/BootstrapBlazor/Components/Table/Table.razor Switches SearchForm usage to OnChanged callback.
src/BootstrapBlazor/Components/Table/InternalTableColumn.cs Renames internal column metadata property and doc reference.
src/BootstrapBlazor/Components/Table/ITableColumn.cs Renames interface property to SearchFormItemMetadata.
src/BootstrapBlazor/Components/Searches/StringSearchMetadata.cs Renames type/base and adds Reset() implementation.
src/BootstrapBlazor/Components/Searches/SelectSearchMetadata.cs Renames select metadata type to *Metadata.
src/BootstrapBlazor/Components/Searches/SearchMetadataBase.cs Renames base/interface and adds abstract Reset().
src/BootstrapBlazor/Components/Searches/NumberSearchMetadata.cs Renames type/base and adds Reset() implementation.
src/BootstrapBlazor/Components/Searches/MultipleStringSearchMetadata.cs Renames type/base and doc cref updates.
src/BootstrapBlazor/Components/Searches/MultipleSelectSearchMetadata.cs Renames type/base and doc cref updates.
src/BootstrapBlazor/Components/Searches/ISearchFormItemMetadata.cs Renames interface and adds Reset() contract.
src/BootstrapBlazor/Components/Searches/DateTimeSearchMetadata.cs Renames type/base and adds Reset() implementation.
src/BootstrapBlazor/Components/Searches/DateTimeRangeSearchMetadata.cs Renames type/base and adds Reset() implementation.
src/BootstrapBlazor/Components/Searches/CheckboxListSearchMetadata.cs Renames type/base to *Metadata.
src/BootstrapBlazor/Components/SearchForm/SearchItem.cs Renames MetaDataMetadata and adds Reset() forwarding.
src/BootstrapBlazor/Components/SearchForm/SearchForm.razor.cs Reworks API to OnChanged, auto-builds metadata, and adds localization options injection.
src/BootstrapBlazor/Components/SearchForm/ISearchItem.cs Renames MetaDataMetadata and adds Reset().
src/BootstrapBlazor/Components/Dialog/SearchDialogOption.cs Adds SearchForm-mode options (UseSearchForm, SearchItems, OnFilterChanged, localizer options).
src/BootstrapBlazor/Components/Dialog/SearchDialog.razor.cs Adds SearchForm-mode params, button rendering reuse, and conditional Items generation.
src/BootstrapBlazor/Components/Dialog/SearchDialog.razor Renders SearchForm when UseSearchForm is enabled and reuses RenderButtons.
src/BootstrapBlazor/Components/Dialog/EditDialog.razor.cs Adds Model null guard and auto-populates Items when no BodyTemplate is provided.
src/BootstrapBlazor/Components/Dialog/DialogBase.cs Removes Model null check; introduces helper to generate Items from model attributes.
src/BootstrapBlazor/Attributes/AutoGenerateColumnAttribute.cs Renames explicit interface metadata property to SearchFormItemMetadata.
src/BootstrapBlazor.Server/wwwroot/css/site.css Adds .bb-grid layout helpers used by updated sample UI.
src/BootstrapBlazor.Server/Locales/zh-CN.json Updates SearchForm docs strings and adds new UI text keys.
src/BootstrapBlazor.Server/Locales/en-US.json Updates SearchForm docs strings and adds new UI text keys.
src/BootstrapBlazor.Server/Components/Samples/Table/TablesSearch.razor.cs Adds sample state for toggling UseSearchForm and defines SearchItems list.
src/BootstrapBlazor.Server/Components/Samples/Table/TablesSearch.razor Adds/updates SearchForm demo and UI switches, and wires UseSearchForm/SearchItems.
Comments suppressed due to low confidence (1)

src/BootstrapBlazor/Components/Searches/MultipleSelectSearchMetadata.cs:18

  • GetFilter() parses MultipleSelect values by splitting on spaces, but MultiSelect/CheckboxList values are formatted as comma-separated strings (e.g., "v1,v2"). This will cause multi-selection filters to be built incorrectly when the value contains commas. Adjust the parsing to split on ',' (and trim) to match the component serialization format.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link

codecov bot commented Mar 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (d3661a7) to head (d32c21f).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main     #7783    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files          764       764            
  Lines        33960     34115   +155     
  Branches      4675      4696    +21     
==========================================
+ Hits         33960     34115   +155     
Flag Coverage Δ
BB 100.00% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ArgoZhang ArgoZhang merged commit 518b85b into main Mar 22, 2026
9 checks passed
@ArgoZhang ArgoZhang deleted the feat-table-search branch March 22, 2026 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(Table): advance search dialog support UseSearchForm parameter

2 participants